
#include "nane/testframework/test1/test1.h"
#include "nane/RenderSystem/Texture.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    const int c_width = 20;
    const int c_height = 20;
    static bool s_field[c_width * c_height] = { false };
    ////////////////////////////////////////////////////////////////////////////////////////////////
    TestFramework* TestFramework::CreateTest()
    {
        return new Test1();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::ReleaseTest( TestFramework* _test )
    {
        delete static_cast<Test1*>( _test );
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::OnSetup()
    {
        m_renderSystem.SetLightingEnable(false);
        m_renderSystem.SetCullMode(ECullMode::CounterClockwise);
        VertexElement vertexDecl[] = {
              {0, 0, EVertexElementSemantic::Position, EVertexElementType::Float3, 0}
            , {0, 12, EVertexElementSemantic::Normal, EVertexElementType::Float3, 0}
            //, {0, 24, VES_COLOR, EVertexElementType::Color, 0}
            , {0, 24, EVertexElementSemantic::TexCoord, EVertexElementType::Float2, 0} 
        };
        struct CUSTOMVERTEX
        {
            float x, y, z;      // The untransformed position for the vertex.
            float nx, ny, nz;   // normal
            float tu, tv;       // texture coords
        };

        CUSTOMVERTEX vertices[] =
        {
            // Front Face
            {-1.0f, 1.0f,-1.0f,  0.0f, 0.0f,-1.0f, 0.0f,0.0f },
            { 1.0f, 1.0f,-1.0f,  0.0f, 0.0f,-1.0f, 1.0f,0.0f },
            {-1.0f,-1.0f,-1.0f,  0.0f, 0.0f,-1.0f, 0.0f,1.0f },
            { 1.0f,-1.0f,-1.0f,  0.0f, 0.0f,-1.0f, 1.0f,1.0f },
            // Back Face
            {-1.0f, 1.0f, 1.0f,  0.0f, 0.0f, 1.0f, 1.0f,0.0f },
            {-1.0f,-1.0f, 1.0f,  0.0f, 0.0f, 1.0f, 1.0f,1.0f },
            { 1.0f, 1.0f, 1.0f,  0.0f, 0.0f, 1.0f, 0.0f,0.0f },
            { 1.0f,-1.0f, 1.0f,  0.0f, 0.0f, 1.0f, 0.0f,1.0f },
            // Top Face
            {-1.0f, 1.0f, 1.0f,  0.0f, 1.0f, 0.0f, 0.0f,0.0f },
            { 1.0f, 1.0f, 1.0f,  0.0f, 1.0f, 0.0f, 1.0f,0.0f },
            {-1.0f, 1.0f,-1.0f,  0.0f, 1.0f, 0.0f, 0.0f,1.0f },
            { 1.0f, 1.0f,-1.0f,  0.0f, 1.0f, 0.0f, 1.0f,1.0f },
            // Bottom Face
            {-1.0f,-1.0f, 1.0f,  0.0f,-1.0f, 0.0f, 0.0f,1.0f },
            {-1.0f,-1.0f,-1.0f,  0.0f,-1.0f, 0.0f, 0.0f,0.0f },
            { 1.0f,-1.0f, 1.0f,  0.0f,-1.0f, 0.0f, 1.0f,1.0f },
            { 1.0f,-1.0f,-1.0f,  0.0f,-1.0f, 0.0f, 1.0f,0.0f },
            // Right Face
            { 1.0f, 1.0f,-1.0f,  1.0f, 0.0f, 0.0f, 0.0f,0.0f },
            { 1.0f, 1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 1.0f,0.0f },
            { 1.0f,-1.0f,-1.0f,  1.0f, 0.0f, 0.0f, 0.0f,1.0f },
            { 1.0f,-1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 1.0f,1.0f },
            // Left Face
            {-1.0f, 1.0f,-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,0.0f },
            {-1.0f,-1.0f,-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,1.0f },
            {-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f,0.0f },
            {-1.0f,-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f,1.0f }
        };

        uint16 indicies[] = { 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15, 14, 16, 17, 18, 17, 19, 18, 20, 21, 22, 21, 23, 22 };

        m_pVB = m_renderSystem.CreateVertexBuffer(EResourceUsage::Default, 24, sizeof(CUSTOMVERTEX), vertexDecl, 3, reinterpret_cast<byte*>(vertices));
        m_renderSystem.SetVertexBuffer(0, m_pVB);

        m_pIB = m_renderSystem.CreateIndexBuffer(EResourceUsage::Default, 36, indicies);
        m_renderSystem.SetIndexBuffer(m_pIB);

        m_pTexture = m_renderSystem.CreateTexture(EResourceUsage::Default, 2, 2, EPixelFormat::A8R8G8B8);
        uint32 pitch = 0;
        byte* textureData = m_pTexture.Lock(0, 0, 2, 2, &pitch);
        if( textureData != NULL )
        {
            memset(textureData, 0xFF, 4 * 2 * 2);
            m_pTexture.Unlock();
        }
        m_renderSystem.SetTexture(0, m_pTexture);
        
        m_constantBuffer = m_renderSystem.CreateConstantBuffer(8);
        m_renderSystem.SetVertexShaderConstantBuffer(0, m_constantBuffer);
        
        m_vs = m_renderSystem.CreateVertexShader(FileSystem::OpenReadFile("assets/test1/VS.bin"));
        if( m_vs == NullPtr )
        {
            LOG_ERROR("Failed to load vertex shader");
        }
        m_renderSystem.SetVertexShader(m_vs);

        m_ps = m_renderSystem.CreatePixelShader(FileSystem::OpenReadFile("assets/test1/PS.bin"));
        if( m_ps == NullPtr )
        {
            LOG_ERROR("Failed to load pixel shader");
        }
        m_renderSystem.SetPixelShader(m_ps);

        //cml::matrix44f_r proj;
        cml::matrix_perspective_xfov_LH(m_proj, cml::constantsf::pi_over_4(), static_cast<float>(m_windowWidth) / m_windowHeight, 1.0f, 100.0f, cml::z_clip_zero);
        //m_renderSystem.setProjectionMatrix(proj.data());

        cml::vector3f vEyePt   ( 20.0f, 15.0f,-60.0f );
        cml::vector3f vLookatPt( 20.0f, 15.0f, 0.0f );
        cml::vector3f vUpVec   ( 0.0f, 1.0f, 0.0f );
        cml::matrix_look_at_LH(m_view, vEyePt, vLookatPt, vUpVec);
        //m_renderSystem.setModelViewMatrix(m_view.data());

        ReadFilePtr luaFile = m_assets.OpenFile("assets/test1/test1.lua");
        if( luaFile != NullPtr )
        {
            m_scriptModule = m_scriptSystem.LoadModule("Test1", luaFile);
            m_scriptModule.ExportMemberFunction("Test1", "SetPixel", &Test1::setPixel, this);
            m_scriptModule.ExportMemberFunction("Test1", "PlayBeep", &Test1::playBeep, this);
            m_scriptModule.ImportObject("main").Invoke<void>("main");
            luaFile = NullPtr;
        }
        
        SoundDecoder soundDecoder =
                SoundCodecManager::Instance("assets/test1/tetris.ogg")->CreateDecoder(m_assets.OpenFile("assets/test1/tetris.ogg"));
        if( soundDecoder != NullPtr )
        {
            SoundBuffer soundBuffer
                    = m_soundSystem.CreateSoundBuffer(ESoundFormat::PCM, soundDecoder.GetFrequency()
                    , soundDecoder.GetChannels(), soundDecoder.GetBitsPerSample(), soundDecoder.GetLengthInBytes());
            if( soundBuffer != NullPtr )
            {
                byte* soundBufferLock = soundBuffer.Lock();
                soundDecoder.Read(soundBufferLock, soundDecoder.GetLengthInBytes());
                soundBuffer.Unlock();
            }
            m_music = m_soundSystem.CreateSoundSource(soundBuffer);
        }
        soundDecoder = SoundCodecManager::Instance("assets/test1/beep.wav")->CreateDecoder(m_assets.OpenFile("assets/test1/beep.wav"));
        if( soundDecoder != NullPtr )
        {
            SoundBuffer soundBuffer
                    = m_soundSystem.CreateSoundBuffer(ESoundFormat::PCM, soundDecoder.GetFrequency()
                    , soundDecoder.GetChannels(), soundDecoder.GetBitsPerSample(), soundDecoder.GetLengthInBytes());
            if( soundBuffer != NullPtr )
            {
                byte* soundBufferLock = soundBuffer.Lock();
                soundDecoder.Read(soundBufferLock, soundDecoder.GetLengthInBytes());
                soundBuffer.Unlock();
            }
            m_beep = m_soundSystem.CreateSoundSource3D(soundBuffer);
        }
        soundDecoder = NullPtr;
        m_music.Play();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::OnUpdate( uint32 _dt )
    {
        m_scriptModule.ImportObject("Update").Invoke<void, uint32>(_dt);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::OnRender()
    {
        m_renderSystem.ClearRenderTarget(EFrameBufferType::Color | EFrameBufferType::Depth, 0xFF00FFFF, 1.0f, 0);
        m_renderSystem.BeginScene();
        for( int i = 0; i < c_width * c_height; ++i )
        {
            if( s_field[i] == true )
            {
                cml::matrix44f_r world;
                int y = i / c_width;
                int x = i - y * c_width;
                cml::matrix_translation(world, x * 2.0f, y * 2.0f);
                cml::matrix44f_r modelviewproj = world * m_view * m_proj;
                //m_renderSystem.setModelViewMatrix(modelviewproj.data());
                //m_renderSystem.setModelViewMatrix(modelview.data());
                m_constantBuffer.SetConstant(0, modelviewproj(0, 0), modelviewproj(1, 0), modelviewproj(2, 0), modelviewproj(3, 0));
                m_constantBuffer.SetConstant(1, modelviewproj(0, 1), modelviewproj(1, 1), modelviewproj(2, 1), modelviewproj(3, 1));
                m_constantBuffer.SetConstant(2, modelviewproj(0, 2), modelviewproj(1, 2), modelviewproj(2, 2), modelviewproj(3, 2));
                m_constantBuffer.SetConstant(3, modelviewproj(0, 3), modelviewproj(1, 3), modelviewproj(2, 3), modelviewproj(3, 3));
                
                m_constantBuffer.SetConstant(4, world(0, 0), world(1, 0), world(2, 0), world(3, 0));
                m_constantBuffer.SetConstant(5, world(0, 1), world(1, 1), world(2, 1), world(3, 1));
                m_constantBuffer.SetConstant(6, world(0, 2), world(1, 2), world(2, 2), world(3, 2));
                m_constantBuffer.SetConstant(7, world(0, 3), world(1, 3), world(2, 3), world(3, 3));
                m_renderSystem.DrawIndexedPrimitive(EPrimitiveType::TriangleList, 0, 0, 24, 0, 12);
            }
        }
        //RenderSystem::drawIndexedPrimitive(PT_TRIANGLELIST, 0, 0, 24, 0, 12);
        m_renderSystem.EndScene();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::OnShutdown()
    {
        m_beep = NullPtr;
        m_constantBuffer = NullPtr;
        m_vs = NullPtr;
        m_ps = NullPtr;
        m_music = NullPtr;
        m_scriptModule = NullPtr;
        m_pTexture = NullPtr;
        m_pIB = NullPtr;
        m_pVB = NullPtr;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::setPixel( int _x, int _y, bool _set )
    {
        if( _y * c_width + _x < c_width * c_height )
        {
            s_field[_y * c_width + _x] = _set;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test1::playBeep(float _x, float _y, float _z)
    {
        m_beep.SetPosition(_x, _y, _z);
        m_beep.Play();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
